Skip to content

test(mapper): strengthen concurrency and nested-AND coverage - #187

Merged
shihyuho merged 2 commits into
jakartafrom
test/182-mapper-test-coverage
Jul 16, 2026
Merged

test(mapper): strengthen concurrency and nested-AND coverage#187
shihyuho merged 2 commits into
jakartafrom
test/182-mapper-test-coverage

Conversation

@shihyuho

Copy link
Copy Markdown
Member

Summary

Strengthens two mapper tests so they actually exercise the code paths they claim to cover (WP10, findings TEST-06 and TEST-05). Test-only change — no production code touched.

  • TEST-06 (concurrency). ReflectionDatabindTest.fireOnlyOnce previously ran a single-threaded pool (numberOfThreads = 1), so the AtomicBoolean CAS + CountDownLatch dedup in ReflectionDatabind.getFieldValue() was never contended and the times(1) assertion would pass even if the dedup were deleted. It now launches 32 threads held at a CyclicBarrier and released together, so multiple threads race into getFieldValue() on the same Databind instances, while still asserting the inner reflective read runs exactly once per bind.
  • TEST-05 (three-level AND). NestedInNestedAnd.name was annotated @NestedSpec String name (a typo vs its OR twin's @Spec), so recursing into a String yielded null and the deepest AND level was silently absent. Changed to @Spec (mirroring NestedInNestedOr) and extended allAnd's assertions to a full three-level Conjunction tree symmetric to allOr's Disjunction tree.

TEST-03 (inherited criteria fields) is deliberately out of scope — it needs a human policy decision and is tracked separately.

Closes #182

Acceptance criteria

  • ReflectionDatabindTest.fireOnlyOnce launches 32 concurrent threads held at a start barrier and released together, so multiple threads call the same ReflectionDatabind.getFieldValue() concurrently.
  • For every Databind returned by ReflectionDatabind.of(...), the test asserts the inner getFieldValue(object, field) read is invoked exactly once (times(1)) despite the concurrent access.
  • The strengthened concurrency test passes deterministically with no production-code change.
  • NestedInNestedAnd.name is annotated @Spec (matching NestedInNestedOr.name).
  • allAnd asserts a three-level Conjunction tree: top-level Conjunction of size 2 (Equals + nested Conjunction), nested Conjunction of size 2 (Equals + deepest Conjunction), deepest Conjunction of size 1 containing an Equals.
  • allAnd's repository result still returns exactly matt.
  • mvn test (mapper module) passes.

Testing

mvn test run on JDK 17 — full reactor green: specification-mapper and specification-mapper-starter both SUCCESS. The two affected classes: ReflectionDatabindTest (1 test) and NestedSpecificationResolverTest (5 tests) pass.

Make ReflectionDatabindTest.fireOnlyOnce launch 32 threads gated on a start
barrier so they genuinely contend on the AtomicBoolean CAS + CountDownLatch
dedup in getFieldValue(), while still asserting the inner reflective read runs
exactly once per Databind.

Fix the NestedInNestedAnd.name annotation from @NestedSpec to @SPEC (mirroring
NestedInNestedOr) so the depth-3 AND scenario actually composes a predicate, and
extend allAnd's assertions to a full three-level Conjunction tree symmetric to
allOr's Disjunction tree.

Refs #182

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>

@shihyuho shihyuho left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE (advisory) — reviewed all 9 axes, no must-fix

Non-blocking (4)

  • Optional · Spec conformance — mapper/src/test/java/tw/com/softleader/data/jpa/spec/ReflectionDatabindTest.java:56Closes #182 will auto-close the issue while TEST-06's second clause (a bind whose reflective read throws) is neither implemented nor declared out of scope.
  • Nit · Correctness — mapper/src/test/java/tw/com/softleader/data/jpa/spec/ReflectionDatabindTest.java:67 — the worker-thread throw new RuntimeException(e) lands in a discarded Future, so fireOnlyOnce stays green even if every loser thread blows up.
  • Nit · Correctness — mapper/src/test/java/tw/com/softleader/data/jpa/spec/ReflectionDatabindTest.java:73startBarrier.await() and done.await() are unbounded, so a regression in the very latch machinery this test guards would hang the build instead of failing it.
  • Nit · Test coverage — mapper/src/test/java/tw/com/softleader/data/jpa/spec/NestedSpecificationResolverTest.java:395 — flipping NestedInNestedAnd.name to @Spec removes the suite's only coverage of NestedSpecificationResolver's null-collapse branch.
Coverage & checks
Axis Status
Correctness 2 findings
Spec conformance 1 finding
Scope clean — every hunk traced to TEST-06/TEST-05 of the linked issue; only the two test files touched
Convention clean — checked the base-ref CLAUDE.md / AGENTS.md / CONTRIBUTING.md; AGENTS.md's only rule (commit hygiene) is CI-enforced and lands on no changed line
Security clean — test-only; no secrets or string-built queries introduced, specs still constructed through the typed Criteria API
Readability clean — the latchdone rename disambiguates the two synchronizers; depth2 matches the allOr/mix idiom already in the file
Architecture clean — NestedInNestedAnd now mirrors NestedInNestedOr; the assertion navigation is byte-identical to the established pattern
Performance clean — production sources re-fetched at the head SHA and confirmed unchanged; 32 threads costs single-digit ms
Test coverage 1 finding
  • Verified — all 4 checks green: commits, pr-title, continuous-integration/jenkins/branch, continuous-integration/jenkins/pr-merge (read via gh pr checks, never run locally)
  • Out-of-band — re-fetched ReflectionDatabind, NestedSpecificationResolver and SpecMapper at the head SHA to confirm no production line changed · traced ThreadPoolExecutor core-thread behaviour to rule out a CyclicBarrier(32) deadlock · confirmed Mockito's invocation recording is synchronized, so the 32-thread verify is race-free

🤖 Reviewed by Claude Opus 4.8

(self-review — same account; GitHub records this as event=COMMENT with no badge, so the verdict above is advisory.)

@shihyuho
shihyuho merged commit 519680a into jakarta Jul 16, 2026
2 of 3 checks passed
@shihyuho
shihyuho deleted the test/182-mapper-test-coverage branch July 16, 2026 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WP10] Mapper test-coverage gaps: concurrency, nested-AND typo, inherited fields

1 participant